import Flutter
import UIKit
import Flutter
import TUICallEngine

public class SwiftTUICallEnginePlugin: NSObject, FlutterPlugin {
    
    static let channelName = "tuicall_engine"
    private let channel: FlutterMethodChannel
    let registrar: FlutterPluginRegistrar
    var messager: FlutterBinaryMessenger {
        return registrar.messenger()
    }
    
    init(registrar: FlutterPluginRegistrar) {
        self.channel = FlutterMethodChannel(name: SwiftTUICallEnginePlugin.channelName, binaryMessenger: registrar.messenger())
        self.registrar = registrar
        
        super.init()
    }

    
    public static func register(with registrar: FlutterPluginRegistrar) {
        let instance = SwiftTUICallEnginePlugin.init(registrar: registrar)
        registrar.addMethodCallDelegate(instance, channel: instance.channel)
    }

    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        result("iOS " + UIDevice.current.systemVersion)
        
        switch call.method {
            case "addObserver":
                addObserver(call: call, result: result)
                break;
            case "removeAddObserver":
                removeObserver(call: call, result: result)
                break;
            default:
                break;
        }
    }
}

// MARK: addObserver、 removeObserver
extension SwiftTUICallEnginePlugin {
    func addObserver(call: FlutterMethodCall, result: @escaping FlutterResult) {
        TUICallEngine.createInstance().addObserver(self)
    }
    func removeObserver(call: FlutterMethodCall, result: @escaping FlutterResult) {
        TUICallEngine.createInstance().removeObserver(self)
    }
}

// MARK: 回调
extension SwiftTUICallEnginePlugin: TUICallObserver {
    
    enum TUICallEigenObserverType: String {
      case onError
      case onCallReceive
      case onCallCancelled
      case onCallBegin
      case onCallEnd
      case onCallMediaTypeChanged
      case onUserReject
      case onUserNoResponse
      case onUserLineBusy
      case onUserJoin
      case onUserLeave
      case onUserVideoAvailable
      case onUserAudioAvailable
      case onUserVoiceVolumeChanged
      case onUserNetworkQualityChanged
      case onKickedOffline
      case onUserSigExpired
    }
    
    func invokeListener(type: TUICallEigenObserverType, params: Any? = nil) {
        var arguments: [String: Any] = [:]
        arguments["method"] = type.rawValue
        if let paramsValue = params {
            arguments["params"] = paramsValue
        }
        channel.invokeMethod("onTUICallObserver", arguments: arguments)
    }

    
    public func onError(code: Int32, message: String?) {
        invokeListener(type: .onError, params: [
            "code": code,
            "message": message ?? ""
        ])
    }
    
    public func onCallReceived(callerId: String, calleeIdList: [String], groupId: String?, callMediaType: TUICallMediaType) {
        invokeListener(type: .onCallReceive, params: [
            "callerId": callerId,
            "calleeIdList": calleeIdList as Any,
            "groupId": groupId ?? "",
            "callMediaType": callMediaType.rawValue
        ])
    }

    public func onCallCancelled(callerId: String) {
        invokeListener(type: .onCallCancelled, params: [
            "callerId": callerId
        ])
    }
    
    public func onCallBegin(roomId: TUIRoomId, callMediaType: TUICallMediaType, callRole: TUICallRole) {
        
        let roomIdParam: [String : UInt32] = ["intRoomId" : roomId.intRoomId]
        invokeListener(type: .onCallBegin, params: [
            "roomId": roomIdParam,
            "callMediaType": callMediaType.rawValue,
            "callRole": callRole.rawValue
        ])
    }
    
    public func onCallEnd(roomId: TUIRoomId, callMediaType: TUICallMediaType, callRole: TUICallRole, totalTime: Float) {
        
        let roomIdParam: [String : UInt32] = ["intRoomId" : roomId.intRoomId]
        invokeListener(type: .onCallEnd, params: [
            "roomId": roomIdParam,
            "callMediaType": callMediaType.rawValue,
            "callRole": callRole.rawValue,
            "totalTime": totalTime
        ])
    }

    public func onCallMediaTypeChanged(oldCallMediaType: TUICallMediaType, newCallMediaType: TUICallMediaType) {
        invokeListener(type: .onCallMediaTypeChanged, params: [
            "oldCallMediaType": oldCallMediaType.rawValue,
            "newCallMediaType": newCallMediaType.rawValue,
        ])
    }
    
    public func onUserReject(userId: String) {
        invokeListener(type: .onUserReject, params: [
            "userId": userId,
        ])
    }
    
    public func onUserNoResponse(userId: String) {
        invokeListener(type: .onUserNoResponse, params: [
            "userId": userId,
        ])
    }
    
    public func onUserLineBusy(userId: String) {
        invokeListener(type: .onUserLineBusy, params: [
            "userId": userId,
        ])
    }
    
    public func onUserJoin(userId: String) {
        invokeListener(type: .onUserJoin, params: [
            "userId": userId,
        ])
    }
    
    public func onUserLeave(userId: String) {
        invokeListener(type: .onUserLeave, params: [
            "userId": userId,
        ])
    }
    
    public func onUserVideoAvailable(userId: String, isVideoAvailable: Bool) {
        invokeListener(type: .onUserVideoAvailable, params: [
            "userId": userId,
            "isVideoAvailable": isVideoAvailable,
        ])
    }
    
    public func onUserAudioAvailable(userId: String, isAudioAvailable: Bool) {
        invokeListener(type: .onUserAudioAvailable, params: [
            "userId": userId,
            "isAudioAvailable": isAudioAvailable,
        ])
    }

    public func onUserVoiceVolumeChanged(volumeMap: [String : NSNumber]) {
        invokeListener(type: .onUserVoiceVolumeChanged, params: [
            "volumeMap": volumeMap,
        ])
    }
    
    public func onUserNetworkQualityChanged(networkQualityList: [TUINetworkQualityInfo]) {        
        var infos: [[String : Any]] = []
        for info in networkQualityList {
            let param: [String : Any] = ["userId" : info.userId, "networkQuality" : info.quality.rawValue]
            infos.append(param)
        }
        
        invokeListener(type: .onUserNetworkQualityChanged, params: [
            "networkQualityList": infos,
        ])
    }
    
    public func onKickedOffline() {
        invokeListener(type: .onKickedOffline)
    }
    
    public func onUserSigExpired() {
            invokeListener(type: .onUserSigExpired)
    }
    
}

public extension Dictionary {
    func toJsonString() -> String? {
        guard let data = try? JSONSerialization.data(withJSONObject: self,
                                                     options: JSONSerialization.WritingOptions.init(rawValue: 0)) else {
            return nil
        }
        guard let jsonStr = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else {
            return nil
        }
        return String(jsonStr)
    }
}
